// - Super Fast Blur v1.1 by Mario Klingemann <http://incubator.quasimondo.com>
// - BlobDetection library
import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;

import processing.video.*;
import blobDetection.*;
import  java.io.*;

Capture cam;
BlobDetection theBlobDetection;

PImage img;
boolean newFrame=false;

int colThresh = 210;
int colPrint = 255;

int blobWidthThresh = 20;
int blobHeightThresh = 20;

int soundDelay = 1000;
Minim minim;
AudioSnippet sound;

boolean soundToggle = false;

// Previous Frame
PImage prevFrame;

ArrayList soundQueue = new ArrayList();
ArrayList savedQueue = new ArrayList();
ArrayList prevQueue = new ArrayList();

int cubeNum = 3;

// ==================================================
// setup()
// ==================================================
void setup()
{
        //set up audio object
        minim = new Minim(this);
	// Size of applet
	size(800, 600);
	// Capture
//	cam = new Capture(this, width, height, "USB 2.0 PC Cam-WDM", 30);
        cam = new Capture(this, width, height, 15);
	// BlobDetection
	// img which will be sent to detection (a smaller copy of the cam frame);
	img = new PImage(80,60); 
	theBlobDetection = new BlobDetection(img.width, img.height);
	theBlobDetection.setPosDiscrimination(true);
	theBlobDetection.setThreshold(0.5f); // will detect bright areas whose luminosity > 0.2f;

        // Create an empty image the same size as the video
        prevFrame = createImage(cam.width,cam.height,RGB);
}

// ==================================================
// captureEvent()
// ==================================================
void captureEvent(Capture cam)
{
	cam.read();
	newFrame = true;
        // Save previous frame for motion detection!!
        
}

// ==================================================
// draw()
// ==================================================
void draw()
{
	if (newFrame)
	{
		newFrame=false;
		image(cam,0,0,width,height);
		img.copy(cam, 0, 0, cam.width, cam.height, 
				0, 0, img.width, img.height);
		//fastblur(img, 2);
		theBlobDetection.computeBlobs(img.pixels);
                
                drawBlobsAndEdges(true,true);
                
                prevFrame.copy(cam,0,0,cam.width,cam.height,0,0,cam.width,cam.height); // Before we read the new frame, we always save the previous frame for comparison!
                prevFrame.updatePixels();
                
                //theColorDetector();
                
                setupBlobQueue();
		
	}
}

void theColorDetector(){
  
  loadPixels();
  cam.loadPixels();
  prevFrame.loadPixels();
  
  // Loop through every pixel column
  for (int x = width-1; x >0; x--) {
    // Loop through every pixel row
    for (int y = 0; y < height; y++) {
      // Use the formula to find the 1D location
      int loc = x + y * width;
      
      int pRed = (int) red(prevFrame.pixels[loc]);
      int pGreen = (int) green(prevFrame.pixels[loc]);
      int pBlue = (int) blue(prevFrame.pixels[loc]);
      
      //red
      if(pRed > colThresh){
        color c = color(255, 0, 0);
        pixels[loc] = c;
      }
      //green
      if(pGreen > colThresh){
        color c = color(0, 255, 0);
        pixels[loc] = c;
      }
      //blue
      if(pBlue > colThresh){
        color c = color(0, 0, 255);
        pixels[loc] = c;
      }
//      //yellow
//      if(pRed > colThresh-50 && pGreen > colThresh-50){
//        color c = color(255, 255, 0);
//        pixels[loc] = c;
//      }
//      //purple
//      if(pRed > colThresh-50 && pBlue > colThresh-50){
//        color c = color(255, 0, 255);
//        pixels[loc] = c;
//      }
//      //cyan
//      if(pBlue > colThresh-50 && pGreen > colThresh-50){
//        color c = color(0, 255, 255);
//        pixels[loc] = c;
//      }
//      int whiteThresh = 240;
//      //white
//      if(pRed > whiteThresh && pBlue > whiteThresh && pGreen > whiteThresh){
//        color c = color(255, 255, 255);
//        pixels[loc] = c;
//      }
      //playFoundSounds();
    }
  }
  updatePixels();
}

// ==================================================
// drawBlobsAndEdges()
// ==================================================
void drawBlobsAndEdges(boolean drawBlobs, boolean drawEdges)
{
	noFill();
	Blob b;
	EdgeVertex eA,eB;
	for (int n=0 ; n<theBlobDetection.getBlobNb() ; n++)
	{
		b=theBlobDetection.getBlob(n);
		if (b!=null)
		{
			// Edges
			if (drawEdges)
			{
				strokeWeight(3);
				stroke(0,255,0);
				for (int m=0;m<b.getEdgeNb();m++)
				{
					eA = b.getEdgeVertexA(m);
					eB = b.getEdgeVertexB(m);
					if (eA !=null && eB !=null)
						line(
							eA.x*width, eA.y*height, 
							eB.x*width, eB.y*height
							);
				}
			}

			// Blobs
			if (drawBlobs)
			{
				strokeWeight(1);
				stroke(255,0,0);
				rect(
					b.xMin*width,b.yMin*height,
					b.w*width,b.h*height
					);
			}

		}

      }
}

// ==================================================
// Super Fast Blur v1.1
// by Mario Klingemann 
// <http://incubator.quasimondo.com>
// ==================================================
void fastblur(PImage img,int radius)
{
 if (radius<1){
    return;
  }
  int w=img.width;
  int h=img.height;
  int wm=w-1;
  int hm=h-1;
  int wh=w*h;
  int div=radius+radius+1;
  int r[]=new int[wh];
  int g[]=new int[wh];
  int b[]=new int[wh];
  int rsum,gsum,bsum,x,y,i,p,p1,p2,yp,yi,yw;
  int vmin[] = new int[max(w,h)];
  int vmax[] = new int[max(w,h)];
  int[] pix=img.pixels;
  int dv[]=new int[256*div];
  for (i=0;i<256*div;i++){
    dv[i]=(i/div);
  }

  yw=yi=0;

  for (y=0;y<h;y++){
    rsum=gsum=bsum=0;
    for(i=-radius;i<=radius;i++){
      p=pix[yi+min(wm,max(i,0))];
      rsum+=(p & 0xff0000)>>16;
      gsum+=(p & 0x00ff00)>>8;
      bsum+= p & 0x0000ff;
    }
    for (x=0;x<w;x++){

      r[yi]=dv[rsum];
      g[yi]=dv[gsum];
      b[yi]=dv[bsum];

      if(y==0){
        vmin[x]=min(x+radius+1,wm);
        vmax[x]=max(x-radius,0);
      }
      p1=pix[yw+vmin[x]];
      p2=pix[yw+vmax[x]];

      rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16;
      gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8;
      bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff);
      yi++;
    }
    yw+=w;
  }

  for (x=0;x<w;x++){
    rsum=gsum=bsum=0;
    yp=-radius*w;
    for(i=-radius;i<=radius;i++){
      yi=max(0,yp)+x;
      rsum+=r[yi];
      gsum+=g[yi];
      bsum+=b[yi];
      yp+=w;
    }
    yi=x;
    for (y=0;y<h;y++){
      pix[yi]=0xff000000 | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum];
      if(x==0){
        vmin[y]=min(y+radius+1,hm)*w;
        vmax[y]=max(y-radius,0)*w;
      }
      p1=x+vmin[y];
      p2=x+vmax[y];

      rsum+=r[p1]-r[p2];
      gsum+=g[p1]-g[p2];
      bsum+=b[p1]-b[p2];

      yi+=w;
    }
  }

}



void setupBlobQueue(){
  
  loadPixels();
  cam.loadPixels();
  prevFrame.loadPixels();
  //delay(1000);
//  System.out.println(theBlobDetection.getBlobNb());
//  for(int i = 0; i<theBlobDetection.getBlobNb(); i++){
//    println(theBlobDetection.getBlob(i).w);
//      if(theBlobDetection.getBlobNb() > 0){
//        println("center x/y pos: " + theBlobDetection.getBlob(0).x*width + " " + 
//          theBlobDetection.getBlob(0).y*height);
//        println(theBlobDetection.getBlob(0).xMin*width + " " + 
//          theBlobDetection.getBlob(0).xMax*width + " " + 
//          theBlobDetection.getBlob(0).yMin*height + " " + 
//          theBlobDetection.getBlob(0).yMax*height);
//      }
//  }

  soundQueue.clear();
  for(int i = 0; i<theBlobDetection.getBlobNb(); i++){
    if((theBlobDetection.getBlob(i).xMax*width - theBlobDetection.getBlob(i).xMin*width) > blobWidthThresh){
      if((theBlobDetection.getBlob(i).yMax*height - theBlobDetection.getBlob(i).yMin*height) > blobHeightThresh){
//        println("big enough " + i);
        int redCount = 0;
        int greenCount = 0;
        int blueCount = 0;
        for(int k = (int)theBlobDetection.getBlob(i).xMin*width; k<theBlobDetection.getBlob(i).xMax*width; k++){
          
          for(int n = (int)theBlobDetection.getBlob(i).yMin*height; n<theBlobDetection.getBlob(i).yMax*height; n++){
            
            int loc = k + n * width;
      
            int pRed = (int) red(prevFrame.pixels[loc]);
            int pGreen = (int) green(prevFrame.pixels[loc]);
            int pBlue = (int) blue(prevFrame.pixels[loc]);
            
            //red
            if(pRed > colThresh){
              color c = color(255, 0, 0);
              pixels[loc] = c;
              redCount++;
            }
            //green
            if(pGreen > colThresh){
              color c = color(0, 255, 0);
              pixels[loc] = c;
              greenCount++;
            }
            //blue
            if(pBlue > colThresh){
              color c = color(0, 0, 255);
              pixels[loc] = c;
              blueCount++;
            }
            
          }
        }
        
        addToQueue(redCount, greenCount, blueCount);
      }
    }
  }
  updatePixels();
  playFoundSounds();
  for(int i = 0; i<soundQueue.size(); i++){
    prevQueue.add(soundQueue.get(i));
  }
  
}

//TODO; when counters are passed in, add certain notes to a queue based on those aformentioned note counts.
void addToQueue(int redCount, int greenCount, int blueCount){
  
  
  //if more red tan green
  if(redCount > greenCount && redCount > blueCount)
    soundQueue.add(0);
  else if( greenCount > redCount && greenCount > blueCount)
    soundQueue.add(1);
  else if(blueCount > redCount && blueCount > greenCount)
    soundQueue.add(2);
  
  //soundQueue.add(1);
  //playFoundSounds();
  
}

//TODO; after addToQueue is done, this function is called to execute each sound from the passed in queue.
void playFoundSounds(){

  String toPrint = "";
  
  for(int i = 0; i<savedQueue.size(); i++){
    if(Integer.parseInt(savedQueue.get(i).toString())==0){
        toPrint+="r ";
      sound = minim.loadSnippet("Sound 1.mp3");
    }
    else if(Integer.parseInt(savedQueue.get(i).toString())==1){
        toPrint+="g ";
      sound = minim.loadSnippet("Sound 2.mp3");

    }
    else if(Integer.parseInt(savedQueue.get(i).toString())==2){
        toPrint+="b ";
      sound = minim.loadSnippet("Sound 3.mp3");
    }
    else if(Integer.parseInt(savedQueue.get(i).toString())==99){
        toPrint+="rest ";
      sound = minim.loadSnippet("rest.mp3");
    }
    if(soundToggle)
      sound.play(); 
    delay(soundDelay); 
  }
  
  for(int i = 0; i<soundQueue.size(); i++){
    if(i ==3)
      break;
    
    
    if(Integer.parseInt(soundQueue.get(i).toString())==0){
        toPrint+="r ";
      sound = minim.loadSnippet("Sound 1.mp3");
    }
    else if(Integer.parseInt(soundQueue.get(i).toString())==1){
        toPrint+="g ";
      sound = minim.loadSnippet("Sound 2.mp3");

    }
    else if(Integer.parseInt(soundQueue.get(i).toString())==2){
        toPrint+="b ";
      sound = minim.loadSnippet("Sound 3.mp3");
    }
    
    else if(Integer.parseInt(soundQueue.get(i).toString())==99){
        toPrint+="rest ";
      sound = minim.loadSnippet("rest.mp3");
    }
    
    if(soundToggle)
      sound.play(); 
    delay(soundDelay);  

  }
  delay(soundDelay);
  println(toPrint);
}

void keyPressed() {
//  background(255);
  if (key == 'b'){
    println("b");
  }
  else if(keyCode == ENTER){
    println("SOUNDS TOGGLED!");
    soundToggle = !soundToggle;
  }
  else if(key == ' '){
    println("space");
    if(soundQueue.size() == 0){
      savedQueue.add(99);
      savedQueue.add(99);
      savedQueue.add(99);
    }
    else if(soundQueue.size() == 1){
      savedQueue.add(soundQueue.get(0));
      savedQueue.add(99);
      savedQueue.add(99);
    }
    else if(soundQueue.size() == 2){
      savedQueue.add(soundQueue.get(0));
      savedQueue.add(soundQueue.get(1));
      savedQueue.add(99);
    }
    else{
      for(int i = 0; i<soundQueue.size(); i++){
      savedQueue.add(soundQueue.get(i));
      }
    }
    
    for(int i = 0; i<savedQueue.size(); i++){
      print(savedQueue.get(i));
    }
  }
}


